package ldh.text.service;

import ldh.text.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class CacheDataService implements CursorDataService<ByteBuffer> {

    private static final Logger LOGGER = LoggerFactory.getLogger(CacheDataService.class);

    private long startPosition = 0;
    private long endPosition = 0;
    private int cacheSize;
    private File file;
    private long fileSize;

    private RandomAccessFile randomAccessFile = null;
    private FileChannel fileChannel;
    private ByteBuffer byteBuffer;

    public CacheDataService(File file, int cacheSize) throws IOException {
        this.file = file;
        this.cacheSize = cacheSize;
        initFileInput();
    }

    private void initFileInput() throws IOException {
        randomAccessFile = new RandomAccessFile(file, "r");
        fileChannel = randomAccessFile.getChannel();
        fileSize = fileChannel.size();
        byteBuffer = ByteBuffer.allocate(cacheSize);
    }

    @Override
    public long dataSize() {
        return fileSize;
    }

    @Override
    public ByteBuffer loadData(long position, int size) throws IOException {
        if (position >= this.dataSize()) {
           return ByteBuffer.wrap(new byte[0]);
        }
        if (size > cacheSize) {
            throw new IllegalArgumentException("获取数据长度不能超过缓存大小");
        }
        if (position < 0 || size < 0) {
            throw new IllegalArgumentException("不能小于0");
        }
        if (position + size > dataSize()) {
            size = (int)(dataSize() - position);
        }
        ByteBuffer data = loadBytesData(position, size);
        int length = data.limit();
        if (length == 0L) return data;
        if (position > endPosition) {
            this.startPosition = position;
            this.endPosition = position + length;
        } else if (position == endPosition) {
            if (endPosition - startPosition + length > cacheSize) {
                startPosition += (endPosition - startPosition + length - cacheSize);
                endPosition += length;
            } else {
                endPosition += length;
            }
        } else if (position >= startPosition) {
            startPosition = position;
            endPosition = position + length;
        } else if (position < startPosition) {
            if (position + length == startPosition) {
                if (endPosition - startPosition + length > cacheSize) {
                    startPosition -= length;
                    endPosition -= (endPosition - startPosition + length - cacheSize);
                } else {
                    startPosition -= length;
                }
            } else {
                startPosition = position;
                endPosition = position + length;
            }
        }
        return data;
    }

    @Override
    public void saveData(String file, byte[] data, int startPosition, int endPosition) {

    }

    @Override
    public void saveData(File file, byte[] data) throws IOException {

    }

    private ByteBuffer loadBytesData(long position, int size) throws IOException {
//        long st = System.currentTimeMillis();
        if(endPosition != position) {
            fileChannel.position(position);
        }

        byteBuffer.clear();
        byteBuffer.limit(size);
        int length = fileChannel.read(byteBuffer);
        byteBuffer.flip();
        if (length != -1) {
            int newLength = StringUtil.lastedLine(byteBuffer, length);
            byteBuffer.limit(newLength);
            if (newLength != length) {
                fileChannel.position(position + newLength);
            }
        }
//        LOGGER.info("time {}", System.currentTimeMillis()-st);
        return byteBuffer;
    }

    @Override
    public ByteBuffer loadNextData(int size) throws IOException {
        if (endPosition == dataSize()) return ByteBuffer.wrap(new byte[0]);
        return loadData(endPosition, size);
    }

    @Override
    public ByteBuffer loadPreData(int size) throws IOException {
        if (startPosition == 0) {
            return ByteBuffer.wrap(new byte[0]);
        } else if (startPosition - size < 0) {
            if (size > endPosition) {
                return loadData(0, (int)endPosition);
            } else {
                return loadData(0, size);
            }
        }
        return loadData(startPosition-size, size);
    }

    @Override
    public long getStartPosition() {
        return startPosition;
    }

    @Override
    public long getEndPosition() {
        return endPosition;
    }

    @Override
    public void close() throws IOException {
        if (fileChannel != null) {
            fileChannel.close();
        }
        if (randomAccessFile != null) {
            randomAccessFile.close();
        }
    }
}
